package volleyball.view;

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.IOException;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.Hashtable;
import java.util.Map;

import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.JTextField;
import javax.swing.JToolBar;
import javax.swing.UIManager;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.filechooser.FileNameExtensionFilter;
import javax.swing.*;

import uk.co.caprica.vlcj.component.EmbeddedMediaPlayerComponent;
import volleyball.model.Game;

import org.yaml.snakeyaml.Yaml;

/**
 * Creates the main GUI for vbstats. GUI contains a media player for playing 
 * a video, a Play/Pause button, a speed slider that allows the speed of the 
 * video to be adjusted, and an upload button that uploads the data collected to 
 * a database.
 * 
 * TODO The GUI records keystrokes by the user and sends them to a 
 * StatisticKeyHandler class to record them. (I bet I can store the game in 
 * this class so that MainPanel never actually sees the Game object that is 
 * holding the stats and therefore there is perfect sepration of View and 
 * Control)
 * @author Joshua Kulhavy-Sutherland
 *
 */
public class MainPanel implements ActionListener, KeyListener, ChangeListener{
	
	private final FileNameExtensionFilter FILTER = new FileNameExtensionFilter(
	        "Supported video files", "mpeg", "mp1v", "mpg1", "PIM1",
	        "mp2v", "mpg2", "vcr2", "hdv1", "hdv2", "hdv3", "mx*n", "mx*p",
	        "MPG4", "MPG3","MP4");
	private JTextField textField;
	private JFrame frame;
	private EmbeddedMediaPlayerComponent mediaPlayerComponent;
	private Game game;
	private JFileChooser fc;
	//Inherent information
    private String playerName = "";
    private String servingPlayer ="";
    private long serveTime;
    
    /**
     * Creates a new MainPanel that holds a vlcj EmbeddedMediaPlayerComponent. 
     * Calls all the methods needed to populate and run the MainPanel. 
     */
    public MainPanel(){
    	//TODO this UIManager seems very powerful, read about it
    	UIManager.put("FileChooser.readOnly", Boolean.TRUE);
    	mediaPlayerComponent = new EmbeddedMediaPlayerComponent();
    	game = new Game();
    	fc = new JFileChooser();
    	fc.setFileFilter(FILTER);
    	buildFrame();
    }
    
    /**
     * 
     * Creates a new MainPanel that holds a media player. Calls all the methods 
     * needed to populate and run the MainPanel. Also starts the video at 
     * videoPath playing.
     * @param videoPath the MainPanel will start this video when it loads
     */
    public MainPanel(String videoPath){
    	this();
    	mediaPlayerComponent.getMediaPlayer().playMedia(videoPath);
    }
   
    /**
     * Builds the initial JFrame and populates it
     */
    protected void buildFrame(){
    	frame = new JFrame("Beach Volleyball Stat Aid");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLocation(100, 100);
    	frame.setSize(1050, 600);
    	frame.add(buildPanel());
    	frame.setVisible(true);
    }
    
    /**
     * Builds the main JPanel and populates it
     * @return The main JPanel
     */
    protected JPanel buildPanel(){
    	JPanel panel = new JPanel(new BorderLayout());
       panel.add(buildToolBar(), BorderLayout.PAGE_START);
       panel.add(mediaPlayerComponent, BorderLayout.CENTER);
       return panel;
    }
    
    /**
     * Creates and populates a tool bar
     * @return JToolBar
     */
    protected JToolBar buildToolBar(){
        JToolBar toolBar = new JToolBar("Still draggable");
        addToolBarButtons(toolBar);
        toolBar.addKeyListener(this);
        toolBar.setFocusable(true);
        return toolBar;
    }
    
    /**
     * Adds buttons to a JToolBar object. Current buttons are a play/pause button
     * an upload button, a speedslider, and an uneditable text field.
     * 
     * @param toolBar The toolbar that will have buttons added to it.
     */
    protected void addToolBarButtons(JToolBar toolBar) {
        toolBar.add(buildButton("Pause", "Pauses the video","pause.png"));
        toolBar.add(buildButton("Upload Statistics", "Uploads the statistics from this game."));
        toolBar.add(buildButton("Open Video","Opens a video for statting."));
        toolBar.add(buildSpeedSlider());
        toolBar.add(buildTextField()); 
    }
    
    /**
     * Makes a JButton with the specified buttonName and toolTipText and 
     * returns it. Note that the ActionCommand will be set to the name of
     * the button.
     * @param buttonName - The name of the button, also becomes the actionCommand
     * @param toolTipText - The tool tip for the button
     * @return a JButton with the the name buttonName, tool tip toolTipText
     *         and an action listener.
     */
    protected JButton buildButton(String buttonName,
                                 String toolTipText) {     
        JButton button = new JButton(buttonName);
    	button.setToolTipText(toolTipText);
        button.setActionCommand(buttonName);
        button.addActionListener(this);
        return button;
    }
    
    protected JButton buildButton(String buttonName,
            					 String toolTipText, String imageName) {     
    	Icon icon = createImageIcon("../../images/"+imageName);
    	JButton button;
    	if (icon == null){
    		button = new JButton(buttonName);
    		button.setToolTipText(toolTipText);
        	button.setActionCommand(buttonName);
        	button.addActionListener(this);
        	return button;
    	} else {
    		button = new JButton(icon);
    		button.setToolTipText(toolTipText);
        	button.setActionCommand(buttonName);
        	button.addActionListener(this);
        	return button;
    	}
    }
    
    protected static ImageIcon createImageIcon(String path) {
        java.net.URL imgURL = MainPanel.class.getResource(path);
        if (imgURL != null) {
            return new ImageIcon(imgURL);
        } else {
            System.err.println("Couldn't find file: " + path);
            return null;
        }
    }
    
    /**
     * Creates a speed slider with 3 speed settings Slow, Normal
     * and Fast.
     * @return JSlider the speed slider
     */
    protected JSlider buildSpeedSlider(){
    	 JSlider videoSpeed = new JSlider(JSlider.HORIZONTAL,0,6,3);
         videoSpeed.addChangeListener(this);
         videoSpeed.setMajorTickSpacing(3);
         videoSpeed.setPaintTicks(true);
         
         Hashtable<Integer,JLabel> labelTable = new Hashtable<Integer,JLabel>();
         labelTable.put( new Integer( 0 ), new JLabel("Slower") );
         labelTable.put( new Integer( 3 ), new JLabel("Normal"));
         labelTable.put( new Integer( 6 ), new JLabel("Faster") );
         videoSpeed.setLabelTable( labelTable );

         videoSpeed.setPaintLabels(true);
         return videoSpeed;
    }
    
    /**
     * Creates a text field for displaying the keystrokes
     * @return the TextField
     */
    protected JTextField buildTextField(){
    	textField = new JTextField();
        textField.setEditable(false);
        textField.addKeyListener(this);
        return textField;
    }
    
    /**
     * Appends the actionDescription for a button to the text field. Will 
     * only display 100 characters. After that it starts erasing characters
     * at the end.
     * @param actionDescription A String detailing the action of a button
     * 
     * TODO modify this to be a much nicer action by action commentary of the game
     *      in a side panel.
     */
    protected void displayResult(String actionDescription) {
    	String text = textField.getText();
        text = actionDescription + text;
        if(text.length()>100){
        	text = text.substring(0, 100);
        }
    	textField.setText(text);
    }
    
    /*
     * This section deals with all StateChanged actions and their helper methods
     * @see javax.swing.event.ChangeListener#stateChanged(javax.swing.event.ChangeEvent)
     */
    /**
     * Deals with changes in the speedslider
     */
	@Override
	public void stateChanged(ChangeEvent e) {
		JSlider source = (JSlider)e.getSource();
		if (!source.getValueIsAdjusting()) {
            int fps = (int)source.getValue();
            mediaPlayerComponent.getMediaPlayer().setRate((float) fps/3);
		}
		textField.requestFocusInWindow();
	}

	/**
	 * Not currently used.
	 */
	@Override
	public void keyPressed(KeyEvent arg0) {	
	}

	/**
	 * Not currently used.
	 */
	@Override
	public void keyReleased(KeyEvent arg0) {	
	}

	/**
	 * Checks if a keystroke is in the config file and
	 * if so it adds it to a Game.
	 * 
	 * TODO move this to its own class with all helper methods
	 */
	@Override
	public void keyTyped(KeyEvent a) {
		char c = a.getKeyChar();
		if (a != null){
			Map<String, String> data = loadYaml();
			String keyStroke = (String) data.get(String.valueOf(c));
			if(keyStroke != null){
				parseStats(keyStroke);
			}
		}
	}
	
	/**
	 * Loads the YAML config file and converts it into a Map
	 * @return A map mapping String -> String
	 * @throws NullPointerException If the YAML file is not found
	 */
	private Map<String,String> loadYaml() throws NullPointerException{
		final String YAML_PATH = "/home/josh/workspace/vbstats/src/config.yml";
		Yaml yaml = new Yaml();
		try {
			InputStream in = new FileInputStream(new File(YAML_PATH));
        	Object o = yaml.load(in);
        	@SuppressWarnings("unchecked")
			Map<String, String> data = (Map<String, String>) o;
        	return data;
		} catch (IOException ioe){
			System.out.println("IO Exception thrown in MainPanel.loadYaml()");
			throw new NullPointerException("Yaml file not found");
		}
	}

	
	/**
	 * Parses the keyStroke to figure out what kind of statistic it is and then adds it 
	 * to the Game object under the appropriate player.
	 * @param keyStroke
	 */
	private void parseStats(String keyStroke){//TODO - make assists inherent
		displayResult(keyStroke);
		if (keyStroke.equals("player1")||keyStroke.equals("player2")||
				keyStroke.equals("player3")||keyStroke.equals("player4")){
    		playerName = keyStroke;
    	} else if (!playerName.equals("")){
    		//If player name is not empty check inherent stats and add the normal stats
    		if (keyStroke.equals("serve")){
    			//if action = serve record the time and player for ace calculation 
    			servingPlayer = playerName;
    			serveTime = mediaPlayerComponent.getMediaPlayer().getTime();
    		} else if (keyStroke.equals("pass0")){
    			//if action = pass0 add an ace to servingPlayer's stat line
    			addStatistic("serveAce",servingPlayer,serveTime);
    		} else if (keyStroke.equals("serveError")){
    			//if the serve is an error, add a serve to teh player's profile. This way serve will = ace's + errors + other attempts at the end
    			addStatistic("serve",playerName,mediaPlayerComponent.getMediaPlayer().getTime());
    		} 
    		addStatistic(keyStroke,playerName,mediaPlayerComponent.getMediaPlayer().getTime());
    	} else {
    	}
	}
	
	/**
	 * An ugly brute force method that adds a Statistic to the appropriate stat
	 * @param statName The name of the statistic to be added, must match the config file's name for that stat
	 * @param playerName The name of the player to be added 
	 * @param time the time the action took place
	 */
	private void addStatistic(String statName, String playerName, float time){
		if (statName.equals("serveAce")){
			game.getPlayer(servingPlayer).addServeAce(serveTime);
		} else if (statName.equals("serve")){
			game.getPlayer(playerName).addServe(time);
		} else if (statName.equals("serveError")){
			game.getPlayer(playerName).addServeError(time);
		} else if (statName.equals("ballHandlingError")){
			game.getPlayer(playerName).addBallHandlingError(time);
		} else if (statName.equals("pass0")){
			game.getPlayer(playerName).addPass0(time);
		} else if (statName.equals("pass1")){
			game.getPlayer(playerName).addPass1(time);
		} else if (statName.equals("pass2")){
			game.getPlayer(playerName).addPass2(time);
		} else if (statName.equals("pass3")){
			game.getPlayer(playerName).addPass3(time);
		} else if (statName.equals("attackKill")){
			game.getPlayer(playerName).addAttackKill(time);
		} else if (statName.equals("attackError")){
			game.getPlayer(playerName).addAttackError(time);
		} else if (statName.equals("attackAttempt")){
			game.getPlayer(playerName).addAttackAttempt(time);
		} else if (statName.equals("dig")){
			game.getPlayer(playerName).addDig(time);
		} else if (statName.equals("block")){
			game.getPlayer(playerName).addBlock(time);
		} else if (statName.equals("blockError")){
			game.getPlayer(playerName).addBlockError(time);
		} else if (statName.equals("blockAttempt")){
			game.getPlayer(playerName).addBlockAttempt(time);
		} else if (statName.equals("assist")){
			game.getPlayer(playerName).addAssist(time);
		}
	}
	
	/*
	 *This section of the code deals with all actionPerformed actions and 
	 *their helper methods that are used. 
	 */
	
	/**
	 * Deals with all the buttons on the toolbar. The play button will play the video
	 * ,pause will pause it, open video opens a video, and upload statistics will open 
	 * up an UploadPanel.
	 */
	//TODO seperarate out control
	@Override
 	public void actionPerformed(ActionEvent a) {
		if (a.getActionCommand().equals("Pause")){
			pauseMediaPlayer(a);
		} else if (a.getActionCommand().equals("Play")){
			playMediaPlayer(a);
		} else if (a.getActionCommand().equals("Upload Statistics")){
			UploadPanel uploadPanel = new UploadPanel(game);
			uploadPanel.openPanel("Upload Statistics");
		} else if (a.getActionCommand().equals("Open Video")){
			int returnVal = fc.showOpenDialog(frame);
            if (returnVal == JFileChooser.APPROVE_OPTION) {
                File file = fc.getSelectedFile();
                //This is where a real application would open the file.
                mediaPlayerComponent.getMediaPlayer().playMedia(file.getPath());
            } 
		}
		textField.requestFocusInWindow();
	}
	
	/**
	 * Pauses the media player and sets the button text to Play
	 * @param buttonClick The action event for the button click
	 */
	private void pauseMediaPlayer(ActionEvent buttonClick){
		mediaPlayerComponent.getMediaPlayer().pause();
		JButton button = (JButton) buttonClick.getSource();
		button.setActionCommand("Play");
		button.setIcon(createImageIcon("../../images/play.png"));
		displayResult(" *VIDEO PAUSED* ");
	}
	
	/**
	 * Plays the media player and sets the button text to Pause
	 * @param buttonClick The action event for the button click
	 */
	private void playMediaPlayer(ActionEvent buttonClick){
		mediaPlayerComponent.getMediaPlayer().play();
		JButton button = (JButton) buttonClick.getSource();
		button.setActionCommand("Pause");
		button.setIcon(createImageIcon("../../images/pause.png"));
		displayResult(" *VIDEO UNPAUSED* ");
	}

}